﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Gmantis.Controls.Extended
{
    public enum DateTimePickerEditMode
    {
        Date,
        Time,
        DateTime
    }

    [TemplateVisualState(Name = "Normal", GroupName = "CommonStates"), 
    TemplateVisualState(Name = "InvalidFocused", GroupName = "ValidationStatesStates"), 
    StyleTypedProperty(Property = "ValidationDecoratorStyle", StyleTargetType = typeof(ValidationDecorator)), 
    TemplatePart(Name = "TimeEditor", Type = typeof(TimeEditor)), 
    TemplatePart(Name = "Root", Type = typeof(FrameworkElement)), 
    TemplateVisualState(Name = "Valid", GroupName = "ValidationStatesStates"), 
    TemplatePart(Name = "DatePicker", Type = typeof(DatePicker)), 
    TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates"), 
    TemplateVisualState(Name = "InvalidUnfocused", GroupName = "ValidationStatesStates"), 
    TemplateVisualState(Name = "Disabled", GroupName = "CommonStates"), 
    TemplateVisualState(Name = "Focused", GroupName = "FocusStates"), 
    TemplateVisualState(Name = "Unfocused", GroupName = "FocusStates")]
    public class DateTimePicker : Control
    {
        // Fields
        internal DatePicker _elementDatePicker;
        internal FrameworkElement _elementRoot;
        internal TimeEditor _elementTimeEditor;
        internal bool _isLoaded;
        private double _lastUsedSeparatorWidth;
        private bool _throwIsMouseOverChanged;
        private bool _updatingControls;
        public static readonly DependencyProperty AllowNullProperty = DependencyProperty.Register("AllowNull", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(false, new PropertyChangedCallback(DateTimePicker.OnAllowNullPropertyChanged)));
        public static readonly DependencyProperty ButtonBackgroundProperty = DependencyProperty.Register("ButtonBackground", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty ButtonForegroundProperty = DependencyProperty.Register("ButtonForeground", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty CaretBrushProperty = DependencyProperty.Register("CaretBrush", typeof(Brush), typeof(DateTimePicker), new PropertyMetadata(null, new PropertyChangedCallback(DateTimePicker.OnCaretBrushPropertyChanged)));
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(DateTimePicker), null);
        public static readonly DependencyProperty CustomDateFormatProperty = DependencyProperty.Register("CustomDateFormat", typeof(string), typeof(DateTimePicker), null);
        public static readonly DependencyProperty CustomTimeFormatProperty = DependencyProperty.Register("CustomTimeFormat", typeof(string), typeof(DateTimePicker), null);
        public static readonly DependencyProperty DateFormatProperty = DependencyProperty.Register("DateFormat", typeof(DatePickerFormat), typeof(DateTimePicker), new PropertyMetadata(DatePickerFormat.Short));
        public static readonly DependencyProperty DateMaskProperty = DependencyProperty.Register("DateMask", typeof(string), typeof(DateTimePicker), new PropertyMetadata(string.Empty));
        internal const string DatePickerElementName = "DatePicker";
        public static readonly DependencyProperty DateTimeProperty = DependencyProperty.Register("DateTime", typeof(DateTime?), typeof(DateTimePicker), new PropertyMetadata(null, new PropertyChangedCallback(DateTimePicker.OnDateTimePropertyChanged)));
        public static readonly DependencyProperty DateWatermarkProperty = DependencyProperty.Register("DateWatermark", typeof(object), typeof(DateTimePicker), null);
        public static readonly DependencyProperty DisabledCuesVisibilityProperty = DependencyProperty.Register("DisabledCuesVisibility", typeof(Visibility), typeof(DateTimePicker), new PropertyMetadata(Visibility.Visible));
        public static readonly DependencyProperty EditModeProperty = DependencyProperty.Register("EditMode", typeof(DateTimePickerEditMode), typeof(DateTimePicker), new PropertyMetadata(DateTimePickerEditMode.DateTime, new PropertyChangedCallback(DateTimePicker.OnEditModePropertyChanged)));
        public static readonly DependencyProperty FirstDayOfWeekProperty = DependencyProperty.Register("FirstDayOfWeek", typeof(DayOfWeek), typeof(DateTimePicker), new PropertyMetadata(DayOfWeek.Sunday, new PropertyChangedCallback(DateTimePicker.OnFirstDayOfWeekPropertyChanged)));
        public static readonly DependencyProperty FocusBrushProperty = DependencyProperty.Register("FocusBrush", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty FocusCuesVisibilityProperty = DependencyProperty.Register("FocusCuesVisibility", typeof(Visibility), typeof(DateTimePicker), new PropertyMetadata(Visibility.Visible));
        internal static readonly DependencyProperty ForceMouseOverProperty = DependencyProperty.Register("ForceMouseOver", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(new PropertyChangedCallback(DateTimePicker.OnForceMouseOverPropertyChanged)));
        private static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(false));
        public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.Register("IsFocused", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(new PropertyChangedCallback(DateTimePicker.OnIsFocusedPropertyChanged)));
        public static readonly DependencyProperty IsMouseOverProperty = DependencyProperty.Register("IsMouseOver", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(new PropertyChangedCallback(DateTimePicker.OnIsMouseOverPropertyChanged)));
        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DateTimePicker), null);
        public static readonly DependencyProperty MaxDateProperty = DependencyProperty.Register("MaxDate", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(System.DateTime.MaxValue, new PropertyChangedCallback(DateTimePicker.OnMaxDatePropertyChanged)));
        public static readonly DependencyProperty MinDatePickerWidthProperty = DependencyProperty.Register("MinDatePickerWidth", typeof(double), typeof(DateTimePicker), null);
        public static readonly DependencyProperty MinDateProperty = DependencyProperty.Register("MinDate", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(System.DateTime.MinValue, new PropertyChangedCallback(DateTimePicker.OnMinDatePropertyChanged)));
        public static readonly DependencyProperty MinTimeEditorWidthProperty = DependencyProperty.Register("MinTimeEditorWidth", typeof(double), typeof(DateTimePicker), null);
        public static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty PromptProperty = DependencyProperty.Register("Prompt", typeof(string), typeof(DateTimePicker), new PropertyMetadata("_"));
        internal const string RootElementName = "Root";
        public static readonly DependencyProperty SelectionBackgroundProperty = DependencyProperty.Register("SelectionBackground", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty SelectionForegroundProperty = DependencyProperty.Register("SelectionForeground", typeof(Brush), typeof(DateTimePicker), null);
        public static readonly DependencyProperty SeparatorWidthProperty = DependencyProperty.Register("SeparatorWidth", typeof(double), typeof(DateTimePicker), new PropertyMetadata(3.0, new PropertyChangedCallback(DateTimePicker.OnSeparatorWidthPropertyChanged)));
        internal const string TimeEditorElementName = "TimeEditor";
        public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register("TimeFormat", typeof(TimeEditorFormat), typeof(DateTimePicker), new PropertyMetadata(TimeEditorFormat.LongTime, new PropertyChangedCallback(DateTimePicker.OnTimeFormatPropertyChanged)));
        public static readonly DependencyProperty TimeIncrementProperty = DependencyProperty.Register("TimeIncrement", typeof(TimeSpan), typeof(DateTimePicker), new PropertyMetadata(TimeSpan.FromMinutes(5.0)));
        public static readonly DependencyProperty TimeMaskProperty = DependencyProperty.Register("TimeMask", typeof(string), typeof(DateTimePicker), new PropertyMetadata(string.Empty));
        public static readonly DependencyProperty TimeWatermarkProperty = DependencyProperty.Register("TimeWatermark", typeof(object), typeof(DateTimePicker), null);
        public static readonly DependencyProperty ValidationDecoratorStyleProperty = DependencyProperty.Register("ValidationDecoratorStyle", typeof(Style), typeof(DateTimePicker), null);

        // Events
        public event EventHandler<NullablePropertyChangedEventArgs<DateTime>> DateTimeChanged;
        public event EventHandler<DatePickerDateValidationErrorEventArgs> DateValidationError;
        public event EventHandler<PropertyChangedEventArgs<bool>> IsMouseOverChanged;
        public event EventHandler<PropertyChangedEventArgs<object>> SelectionCommitted;

        // Methods
        public DateTimePicker()
        {
            RoutedEventHandler handler = null;
            this._throwIsMouseOverChanged = true;
            base.DefaultStyleKey = typeof(DateTimePicker);
            if (handler == null)
            {
                handler = (param0, param1) => this.ChangeVisualStateFocus(false);
            }
            base.Loaded += handler;
            base.GotFocus += new RoutedEventHandler(this.Control_GotFocus);
            base.LostFocus += new RoutedEventHandler(this.Control_LostFocus);
            this.SetCustomDefaultValues();
        }

        private void Control_GotFocus(object sender, RoutedEventArgs e)
        {
            this.IsFocused = true;
        }

        private void Control_LostFocus(object sender, RoutedEventArgs e)
        {
            this.IsFocused = false;
        }

        private void Control_MouseEnter(object sender, MouseEventArgs e)
        {
            this.IsMouseOver = true;
        }

        private void Control_MouseLeave(object sender, MouseEventArgs e)
        {
            this.IsMouseOver = false;
        }

        protected void ChangeVisualStateCommon(bool useTransitions)
        {
            if (base.IsEnabled)
            {
                VisualStateHelper.GoToState(this, "MouseOver", useTransitions);
            }
            if (!base.IsEnabled)
            {
                VisualStateHelper.GoToState(this, "Disabled", useTransitions);
            }
            if ((base.IsEnabled && !this.IsMouseOver) && !this.ForceMouseOver)
            {
                VisualStateHelper.GoToState(this, "Normal", useTransitions);
            }
        }

        protected void ChangeVisualStateFocus(bool useTransitions)
        {
            if (!this.IsFocused)
            {
                VisualStateHelper.GoToState(this, "Unfocused", useTransitions);
            }
            if (this.IsFocused)
            {
                VisualStateHelper.GoToState(this, "Focused", useTransitions);
            }
        }

        private static Brush CloneBrush(Brush source)
        {
            Brush brush = null;
            if (source != null)
            {
                if (source is SolidColorBrush)
                {
                    SolidColorBrush brush2 = source as SolidColorBrush;
                    return new SolidColorBrush(brush2.Color);
                }
                if (!(source is GradientBrush))
                {
                    return brush;
                }
                GradientBrush brush3 = source as GradientBrush;
                if (source is LinearGradientBrush)
                {
                    LinearGradientBrush brush4 = source as LinearGradientBrush;
                    LinearGradientBrush brush5 = new LinearGradientBrush
                    {
                        StartPoint = brush4.StartPoint,
                        EndPoint = brush4.EndPoint
                    };
                    brush = brush5;
                }
                else
                {
                    RadialGradientBrush brush6 = source as RadialGradientBrush;
                    RadialGradientBrush brush7 = new RadialGradientBrush
                    {
                        Center = brush6.Center,
                        GradientOrigin = brush6.GradientOrigin,
                        RadiusX = brush6.RadiusX,
                        RadiusY = brush6.RadiusY
                    };
                    brush = brush7;
                }
                GradientBrush brush8 = brush as GradientBrush;
                brush8.ColorInterpolationMode = brush3.ColorInterpolationMode;
                brush8.MappingMode = brush3.MappingMode;
                brush8.RelativeTransform = brush3.RelativeTransform;
                brush8.SpreadMethod = brush3.SpreadMethod;
                brush8.Transform = brush3.Transform;
                foreach (GradientStop stop in brush3.GradientStops)
                {
                    GradientStop stop2 = new GradientStop
                    {
                        Offset = stop.Offset,
                        Color = stop.Color
                    };
                    brush8.GradientStops.Add(stop2);
                }
            }
            return brush;
        }

        public void FinalizeEditing()
        {
            if ((this._elementTimeEditor != null) && (this._elementTimeEditor.Visibility == Visibility.Visible))
            {
                this._elementTimeEditor.FinalizeEditing();
            }
            if ((this._elementDatePicker != null) && (this._elementDatePicker.Visibility == Visibility.Visible))
            {
                this._elementDatePicker.FinalizeEditing();
                if (!this._elementDatePicker.SelectedDate.HasValue)
                {
                    this.UpdateValue(true);
                }
                else
                {
                    this.UpdateValue(false);
                }
            }
        }

        private T GetTemplateChild<T>(string childName, bool required, ref string errors) where T : class
        {
            DependencyObject templateChild = base.GetTemplateChild(childName);
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "template part", childName, templateChild, required, ref errors);
            return (templateChild as T);
        }

        private static Storyboard GetTemplateChildResource(FrameworkElement root, string resourceName, bool required, ref string errors)
        {
            return GetTemplateChildResource<Storyboard>(root, resourceName, required, ref errors);
        }

        private static T GetTemplateChildResource<T>(FrameworkElement root, string resourceName, bool required, ref string errors) where T : class
        {
            object child = root.Resources[resourceName];
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "resource", resourceName, child, required, ref errors);
            return (child as T);
        }

        private void InitializeDatePickerPart()
        {
            this._elementDatePicker.BindInputMethodProperties(this);
            this.BlackoutDates = this._elementDatePicker.BlackoutDates;
            this._elementDatePicker.FirstDayOfWeek = this.FirstDayOfWeek;
            this._elementDatePicker.SelectedDateChanged += delegate(object param0, SelectionChangedEventArgs param1)
            {
                if (!this._elementDatePicker.SelectedDate.HasValue)
                {
                    this.UpdateValue(true);
                }
                else
                {
                    this.UpdateValue(false);
                }
            };
            this._elementDatePicker.DateValidationError += delegate(object sender, DatePickerDateValidationErrorEventArgs e)
            {
                if (this.DateValidationError != null)
                {
                    this.DateValidationError(sender, e);
                }
            };
            this._elementDatePicker.CalendarClosed += (param0, param1) => base.SetValue(IsDropDownOpenProperty, false);
            this._elementDatePicker.CalendarOpened += (param0, param1) => base.SetValue(IsDropDownOpenProperty, true);
            this._elementDatePicker.SelectionCommitted += (param0, param1) => this.OnSelectionCommitted();
        }

        private void InitializeTimeEditorPart()
        {
            this._elementTimeEditor.BindInputMethodProperties(this);
            this._elementTimeEditor.CycleChangesOnBoundaries = true;
            this._elementTimeEditor.ValueChanged += delegate(object param0, NullablePropertyChangedEventArgs<TimeSpan> param1)
            {
                DateTime? dateTime = this.DateTime;
                this.UpdateValue(false);
                if (dateTime != this.DateTime)
                {
                    this.OnSelectionCommitted();
                }
                else if (!this._elementTimeEditor.Value.HasValue && this.DateTime.HasValue)
                {
                    this.UpdateControls();
                }
            };
            this._elementTimeEditor.TextValidationError += delegate(object sender, TextValidationErrorEventArgs e)
            {
                if (this.DateValidationError != null)
                {
                    DatePickerDateValidationErrorEventArgs args = new DatePickerDateValidationErrorEventArgs(e.Exception, e.Text);
                    this.DateValidationError(sender, args);
                    e.ThrowException = args.ThrowException;
                }
            };
        }

        private void OnAfterApplyTemplate()
        {
            this.ChangeVisualStateCommon(false);
            this.OnMaxDateChanged(this.MaxDate);
            this.OnMinDateChanged(this.MinDate);
            this.OnEditModeChanged(DateTimePickerEditMode.DateTime);
            this.OnTimeFormatChanged(TimeEditorFormat.LongTime);
            this.OnCaretBrushChanged();
        }

        private void OnAllowNullChanged(bool oldValue)
        {
            if (!this.AllowNull && !this.DateTime.HasValue)
            {
                this.OnDateTimeChanged((DateTime?)null);
            }
        }

        private static void OnAllowNullPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            bool oldValue = (bool)e.OldValue;
            picker.OnAllowNullChanged(oldValue);
        }

        public override void OnApplyTemplate()
        {
            string errors = string.Empty;
            base.OnApplyTemplate();
            this._isLoaded = true;
            this._elementDatePicker = this.GetTemplateChild<DatePicker>("DatePicker", true, ref errors);
            if (this._elementDatePicker != null)
            {
                this.InitializeDatePickerPart();
            }
            this._elementTimeEditor = this.GetTemplateChild<TimeEditor>("TimeEditor", true, ref errors);
            if (this._elementTimeEditor != null)
            {
                this.InitializeTimeEditorPart();
            }
            this._elementRoot = this.GetTemplateChild<FrameworkElement>("Root", false, ref errors);
            if (!string.IsNullOrEmpty(errors))
            {
                this._isLoaded = false;
                if ((base.Template != null) && !PlatformIndependent.IsInDesignMode(this))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Template cannot be applied to DateTimePicker.\nDetails: {0}", new object[] { errors }));
                }
            }
            else
            {
                this.ChangeVisualStateFocus(false);
                this.OnAfterApplyTemplate();
            }
        }

        private void OnCaretBrushChanged()
        {
            if (this._elementTimeEditor != null)
            {
                this._elementTimeEditor.CaretBrush = CloneBrush(this.CaretBrush);
            }
            if (this._elementDatePicker != null)
            {
                this._elementDatePicker.CaretBrush = CloneBrush(this.CaretBrush);
            }
        }

        private static void OnCaretBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DateTimePicker).OnCaretBrushChanged();
        }

        protected virtual void OnDateTimeChanged(NullablePropertyChangedEventArgs<DateTime> e)
        {
            if (this.DateTimeChanged != null)
            {
                this.DateTimeChanged(this, e);
            }
        }

        private void OnDateTimeChanged(DateTime? oldValue)
        {
            DateTime? dateTime = this.DateTime;
            if (!dateTime.HasValue && !this.AllowNull)
            {
                dateTime = new DateTime?(System.DateTime.Today);
            }
            else
            {
                DateTime? nullable2 = dateTime;
                DateTime minDate = this.MinDate;
                if (nullable2.HasValue ? (nullable2.GetValueOrDefault() < minDate) : false)
                {
                    dateTime = new DateTime?(this.MinDate);
                }
                DateTime? nullable3 = dateTime;
                DateTime maxDate = this.MaxDate;
                if (nullable3.HasValue ? (nullable3.GetValueOrDefault() > maxDate) : false)
                {
                    dateTime = new DateTime?(this.MaxDate);
                }
            }
            if (dateTime.HasValue && (dateTime != this.DateTime))
            {
                base.SetValue(DateTimeProperty, dateTime);
            }
            else
            {
                this.UpdateControls();
                if (oldValue != this.DateTime)
                {
                    NullablePropertyChangedEventArgs<DateTime> e = new NullablePropertyChangedEventArgs<DateTime>
                    {
                        OldValue = oldValue,
                        NewValue = this.DateTime
                    };
                    this.OnDateTimeChanged(e);
                }
            }
        }

        private static void OnDateTimePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            DateTime? oldValue = (DateTime?)e.OldValue;
            picker.OnDateTimeChanged(oldValue);
        }

        private void OnEditModeChanged(DateTimePickerEditMode oldValue)
        {
            if (this._isLoaded)
            {
                Thickness margin = this._elementTimeEditor.Margin;
                if (this._lastUsedSeparatorWidth > 0.0)
                {
                    margin = new Thickness(margin.Left - this._lastUsedSeparatorWidth, margin.Top, margin.Right, margin.Bottom);
                    this._lastUsedSeparatorWidth = 0.0;
                }
                switch (this.EditMode)
                {
                    case DateTimePickerEditMode.Date:
                        this._elementDatePicker.Visibility = Visibility.Visible;
                        this._elementTimeEditor.Visibility = Visibility.Collapsed;
                        this._elementTimeEditor.Margin = margin;
                        break;

                    case DateTimePickerEditMode.Time:
                        this._elementDatePicker.Visibility = Visibility.Collapsed;
                        this._elementTimeEditor.Visibility = Visibility.Visible;
                        this._elementTimeEditor.Margin = margin;
                        break;

                    case DateTimePickerEditMode.DateTime:
                        this._elementDatePicker.Visibility = Visibility.Visible;
                        this._elementTimeEditor.Visibility = Visibility.Visible;
                        this._elementTimeEditor.Margin = new Thickness(margin.Left + this.SeparatorWidth, margin.Top, margin.Right, margin.Bottom);
                        this._lastUsedSeparatorWidth = this.SeparatorWidth;
                        break;
                }
                this.OnTimeFormatChanged(this.TimeFormat);
            }
        }

        private static void OnEditModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            DateTimePickerEditMode oldValue = (DateTimePickerEditMode)e.OldValue;
            picker.OnEditModeChanged(oldValue);
        }

        private void OnFirstDayOfWeekChanged(DayOfWeek oldValue)
        {
            if (this._elementDatePicker != null)
            {
                this._elementDatePicker.FirstDayOfWeek = this.FirstDayOfWeek;
            }
        }

        private static void OnFirstDayOfWeekPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            DayOfWeek oldValue = (DayOfWeek)e.OldValue;
            picker.OnFirstDayOfWeekChanged(oldValue);
        }

        private static void OnForceMouseOverPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DateTimePicker).ChangeVisualStateCommon(true);
        }

        private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DateTimePicker).ChangeVisualStateFocus(true);
        }

        private static void OnIsMouseOverPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker sender = d as DateTimePicker;
            if ((sender.IsMouseOverChanged != null) && sender._throwIsMouseOverChanged)
            {
                PropertyChangedEventArgs<bool> args = new PropertyChangedEventArgs<bool>
                {
                    OldValue = (bool)e.OldValue,
                    NewValue = (bool)e.NewValue
                };
                sender.IsMouseOverChanged(sender, args);
            }
            sender.ChangeVisualStateCommon(true);
        }

        private void OnMaxDateChanged(DateTime oldValue)
        {
            if (this.MinDate > this.MaxDate)
            {
                this.MaxDate = this.MinDate;
            }
            if (this._elementDatePicker != null)
            {
                this._elementDatePicker.DisplayDateEnd = new DateTime?(this.MaxDate);
            }
            this.OnDateTimeChanged(this.DateTime);
        }

        private static void OnMaxDatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            DateTime oldValue = (DateTime)e.OldValue;
            picker.OnMaxDateChanged(oldValue);
        }

        private void OnMinDateChanged(DateTime oldValue)
        {
            if (this.MinDate > this.MaxDate)
            {
                this.MaxDate = this.MinDate;
            }
            if (this._elementDatePicker != null)
            {
                this._elementDatePicker.DisplayDateStart = new DateTime?(this.MinDate);
            }
            this.OnDateTimeChanged(this.DateTime);
        }

        private static void OnMinDatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            DateTime oldValue = (DateTime)e.OldValue;
            picker.OnMinDateChanged(oldValue);
        }

        protected virtual void OnSelectionCommitted()
        {
            if (this.SelectionCommitted != null)
            {
                PropertyChangedEventArgs<object> e = new PropertyChangedEventArgs<object>
                {
                    NewValue = this.DateTime
                };
                this.SelectionCommitted(this, e);
            }
        }

        private void OnSeparatorWidthChanged(double oldValue)
        {
            if (this._lastUsedSeparatorWidth != 0.0)
            {
                Thickness margin = this._elementTimeEditor.Margin;
                margin = new Thickness((margin.Left - this._lastUsedSeparatorWidth) + this.SeparatorWidth, margin.Top, margin.Right, margin.Bottom);
                this._lastUsedSeparatorWidth = this.SeparatorWidth;
                this._elementTimeEditor.Margin = margin;
            }
        }

        private static void OnSeparatorWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            double oldValue = (double)e.OldValue;
            picker.OnSeparatorWidthChanged(oldValue);
        }

        private void OnTimeFormatChanged(TimeEditorFormat oldValue)
        {
            if (this.TimeFormat == TimeEditorFormat.TimeSpan)
            {
                this.TimeFormat = (oldValue != TimeEditorFormat.TimeSpan) ? oldValue : TimeEditorFormat.LongTime;
            }
        }

        private static void OnTimeFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimePicker picker = d as DateTimePicker;
            TimeEditorFormat oldValue = (TimeEditorFormat)e.OldValue;
            picker.OnTimeFormatChanged(oldValue);
        }

        private void SetCustomDefaultValues()
        {
            base.TabNavigation = KeyboardNavigationMode.Local;
            base.Loaded += (param0, param1) => this.ChangeVisualStateCommon(false);
            base.IsEnabledChanged += (s, a) => this.ChangeVisualStateCommon(true);
            base.MouseEnter += new MouseEventHandler(this.Control_MouseEnter);
            base.MouseLeave += new MouseEventHandler(this.Control_MouseLeave);
            this.FirstDayOfWeek = TimeEditor.GetCurrentDateTimeFormat(this).FirstDayOfWeek;
            base.IsTabStop = false;
        }

        private void UpdateControls()
        {
            if (this._isLoaded)
            {
                this._updatingControls = true;
                if (this.DateTime.HasValue)
                {
                    this._elementDatePicker.SelectedDate = new DateTime?(this.DateTime.Value.Date);
                    this._elementTimeEditor.Value = new TimeSpan?(this.DateTime.Value.TimeOfDay);
                }
                else
                {
                    this._elementDatePicker.SelectedDate = null;
                    this._elementTimeEditor.Value = null;
                }
                this._updatingControls = false;
            }
        }

        private void UpdateValue(bool forceNull)
        {
            if (!this._updatingControls)
            {
                DateTime? selectedDate = new DateTime?(System.DateTime.Today);
                if (this.AllowNull)
                {
                    selectedDate = this._elementDatePicker.SelectedDate;
                    if (this._elementTimeEditor.Value.HasValue && !forceNull)
                    {
                        if (selectedDate.HasValue)
                        {
                            if (selectedDate.Value.TimeOfDay == TimeSpan.Zero)
                            {
                                selectedDate = new DateTime?(selectedDate.Value.Add(this._elementTimeEditor.Value.Value));
                            }
                        }
                        else
                        {
                            selectedDate = new DateTime?(System.DateTime.Today.Add(this._elementTimeEditor.Value.Value));
                        }
                    }
                }
                else if (forceNull)
                {
                    selectedDate = this.DateTime;
                }
                else
                {
                    if (this._elementDatePicker.SelectedDate.HasValue)
                    {
                        selectedDate = new DateTime?(this._elementDatePicker.SelectedDate.Value);
                    }
                    if (this._elementTimeEditor.Value.HasValue && (selectedDate.Value.TimeOfDay == TimeSpan.Zero))
                    {
                        selectedDate = new DateTime?(selectedDate.Value.Add(this._elementTimeEditor.Value.Value));
                    }
                }
                this.DateTime = selectedDate;
                this.UpdateControls();
            }
        }

        // Properties
        public bool AllowNull
        {
            get { return (bool)GetValue(AllowNullProperty); }
            set { SetValue(AllowNullProperty, value); }
        }

        public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }

        public Brush ButtonBackground
        {
            get { return (Brush)GetValue(ButtonBackgroundProperty); }
            set { SetValue(ButtonBackgroundProperty, value); }
        }

        public Brush ButtonForeground
        {
            get { return (Brush)GetValue(ButtonForegroundProperty); }
            set { SetValue(ButtonForegroundProperty, value); }
        }

        public Brush CaretBrush
        {
            get { return (Brush)GetValue(CaretBrushProperty); }
            set { SetValue(CaretBrushProperty, value); }
        }

        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public string CustomDateFormat
        {
            get { return (string)GetValue(CustomDateFormatProperty); }
            set { SetValue(CustomDateFormatProperty, value); }
        }

        public string CustomTimeFormat
        {
            get { return (string)GetValue(CustomTimeFormatProperty); }
            set { SetValue(CustomTimeFormatProperty, value); }
        }

        public DatePickerFormat DateFormat
        {
            get { return (DatePickerFormat)GetValue(DateFormatProperty); }
            set { SetValue(DateFormatProperty, value); }
        }

        public string DateMask
        {
            get { return (string)GetValue(DateMaskProperty); }
            set { SetValue(DateMaskProperty, value); }
        }

        [TypeConverter(typeof(NullableDateTimeConverter))]
        public DateTime? DateTime
        {
            get { return (DateTime?)GetValue(DateTimeProperty); }
            set { SetValue(DateTimeProperty, value); }
        }

        public object DateWatermark
        {
            get { return GetValue(DateWatermarkProperty); }
            set { SetValue(DateWatermarkProperty, value); }
        }

        public Visibility DisabledCuesVisibility
        {
            get { return (Visibility)GetValue(DisabledCuesVisibilityProperty); }
            set { SetValue(DisabledCuesVisibilityProperty, value); }
        }

        public DateTimePickerEditMode EditMode
        {
            get { return (DateTimePickerEditMode)GetValue(EditModeProperty); }
            set { SetValue(EditModeProperty, value); }
        }

        public DayOfWeek FirstDayOfWeek
        {
            get { return (DayOfWeek)GetValue(FirstDayOfWeekProperty); }
            set { SetValue(FirstDayOfWeekProperty, value); }
        }

        public Brush FocusBrush
        {
            get { return (Brush)GetValue(FocusBrushProperty); }
            set { SetValue(FocusBrushProperty, value); }
        }

        public Visibility FocusCuesVisibility
        {
            get { return (Visibility)GetValue(FocusCuesVisibilityProperty); }
            set { SetValue(FocusCuesVisibilityProperty, value); }
        }

        internal bool ForceMouseOver
        {
            get { return (bool)GetValue(ForceMouseOverProperty); }
            set { SetValue(ForceMouseOverProperty, value); }
        }

        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
        }

        public bool IsFocused
        {
            get { return (bool)GetValue(IsFocusedProperty); }
            internal set { SetValue(IsFocusedProperty, value); }
        }

        public bool IsMouseOver
        {
            get { return (bool)GetValue(IsMouseOverProperty); }
            internal set { SetValue(IsMouseOverProperty, value); }
        }

        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }

        [TypeConverter(typeof(DateTimeTypeConverter))]
        public DateTime MaxDate
        {
            get { return (DateTime)GetValue(MaxDateProperty); }
            set { SetValue(MaxDateProperty, value); }
        }

        [TypeConverter(typeof(DateTimeTypeConverter))]
        public DateTime MinDate
        {
            get { return (DateTime)GetValue(MinDateProperty); }
            set { SetValue(MinDateProperty, value); }
        }

        public double MinDatePickerWidth
        {
            get { return (double)GetValue(MinDatePickerWidthProperty); }
            set { SetValue(MinDatePickerWidthProperty, value); }
        }

        public double MinTimeEditorWidth
        {
            get { return (double)GetValue(MinTimeEditorWidthProperty); }
            set { SetValue(MinTimeEditorWidthProperty, value); }
        }

        public Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }

        public Brush PressedBrush
        {
            get { return (Brush)GetValue(PressedBrushProperty); }
            set { SetValue(PressedBrushProperty, value); }
        }

        public string Prompt
        {
            get { return (string)GetValue(PromptProperty); }
            set { SetValue(PromptProperty, value); }
        }

        public Brush SelectionBackground
        {
            get { return (Brush)GetValue(SelectionBackgroundProperty); }
            set { SetValue(SelectionBackgroundProperty, value); }
        }

        public Brush SelectionForeground
        {
            get { return (Brush)GetValue(SelectionForegroundProperty); }
            set { SetValue(SelectionForegroundProperty, value); }
        }

        public double SeparatorWidth
        {
            get { return (double)GetValue(SeparatorWidthProperty); }
            set { SetValue(SeparatorWidthProperty, value); }
        }

        public TimeEditorFormat TimeFormat
        {
            get { return (TimeEditorFormat)GetValue(TimeFormatProperty); }
            set { SetValue(TimeFormatProperty, value); }
        }

        public TimeSpan TimeIncrement
        {
            get { return (TimeSpan)GetValue(TimeIncrementProperty); }
            set { SetValue(TimeIncrementProperty, value); }
        }

        public string TimeMask
        {
            get { return (string)GetValue(TimeMaskProperty); }
            set { SetValue(TimeMaskProperty, value); }
        }

        public object TimeWatermark
        {
            get { return GetValue(TimeWatermarkProperty); }
            set { SetValue(TimeWatermarkProperty, value); }
        }

        public Style ValidationDecoratorStyle
        {
            get { return (Style)GetValue(ValidationDecoratorStyleProperty); }
            set { SetValue(ValidationDecoratorStyleProperty, value); }
        }
    }
}
